/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "plugins/ecmascript/runtime/interpreter/slow_runtime_stub.h"

#include "js_tagged_value.h"
#include "lexical_env.h"
#include "plugins/ecmascript/runtime/base/number_helper.h"
#include "plugins/ecmascript/runtime/builtins/builtins_regexp.h"
#include "plugins/ecmascript/runtime/class_linker/program_object-inl.h"
#include "plugins/ecmascript/runtime/ecma_module.h"
#include "plugins/ecmascript/runtime/global_dictionary-inl.h"
#include "plugins/ecmascript/runtime/ic/profile_type_info.h"
#include "plugins/ecmascript/runtime/internal_call_params.h"
#include "plugins/ecmascript/runtime/interpreter/ecma-interpreter.h"
#include "plugins/ecmascript/runtime/interpreter/fast_runtime_stub-inl.h"
#include "plugins/ecmascript/runtime/interpreter/slow_runtime_helper.h"
#include "plugins/ecmascript/runtime/js_arguments.h"
#include "plugins/ecmascript/runtime/js_array.h"
#include "plugins/ecmascript/runtime/js_array_iterator.h"
#include "plugins/ecmascript/runtime/js_async_from_sync_iterator_object.h"
#include "plugins/ecmascript/runtime/js_async_function.h"
#include "plugins/ecmascript/runtime/js_async_generator_object.h"
#include "plugins/ecmascript/runtime/js_for_in_iterator.h"
#include "plugins/ecmascript/runtime/js_function.h"
#include "plugins/ecmascript/runtime/js_generator_object.h"
#include "plugins/ecmascript/runtime/js_hclass-inl.h"
#include "plugins/ecmascript/runtime/js_invoker.h"
#include "plugins/ecmascript/runtime/js_iterator.h"
#include "plugins/ecmascript/runtime/js_promise.h"
#include "plugins/ecmascript/runtime/js_proxy.h"
#include "plugins/ecmascript/runtime/js_tagged_value-inl.h"
#include "plugins/ecmascript/runtime/js_thread.h"
#include "plugins/ecmascript/runtime/linked_hash_table-inl.h"
#include "plugins/ecmascript/runtime/tagged_dictionary.h"
#include "plugins/ecmascript/runtime/runtime_call_id.h"
#include "plugins/ecmascript/runtime/template_string.h"
#include "plugins/ecmascript/runtime/vmstat/runtime_stat.h"
#include "libpandabase/utils/utf.h"

namespace ark::ecmascript {
JSTaggedValue SlowRuntimeStub::CallSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue obj,
                                             JSTaggedValue array)
{
    INTERPRETER_TRACE(thread, CallSpreadDyn);
    if ((!obj.IsUndefined() && !obj.IsECMAObject()) || !func.IsJSFunction() || !array.IsJSArray()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "cannot Callspread", JSTaggedValue::Exception());
    }
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSFunction> jsFunc(thread, func);
    JSHandle<JSTaggedValue> jsArray(thread, array);
    JSHandle<JSTaggedValue> taggedObj(thread, obj);

    JSHandle<TaggedArray> coretypesArray(thread, GetCallSpreadArgs(thread, jsArray.GetTaggedValue()));

    auto info = NewRuntimeCallInfo(thread, jsFunc, taggedObj, JSTaggedValue::Undefined(), coretypesArray->GetLength());
    info->SetCallArg(coretypesArray->GetLength(), coretypesArray->GetData());
    return InvokeJsFunction(info.Get());
}

JSTaggedValue SlowRuntimeStub::NegDyn(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, NegDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> input(thread, value);
    JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, input);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    if (inputVal->IsBigInt()) {
        JSHandle<BigInt> bigValue(inputVal);
        return BigInt::UnaryMinus(thread, bigValue).GetTaggedValue();
    }

    if (inputVal->IsInt()) {
        int32_t intValue = inputVal->GetInt();
        if (intValue == 0) {
            return JSTaggedValue(-0.0);
        }
        return JSTaggedValue(-intValue);
    }
    if (inputVal->IsDouble()) {
        return JSTaggedValue(-inputVal->GetDouble());
    }

    UNREACHABLE();
}

JSTaggedValue SlowRuntimeStub::AsyncFunctionEnter(JSThread *thread)
{
    INTERPRETER_TRACE(thread, AsyncFunctionEnter);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    // 1. create promise
    JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<JSTaggedValue> promiseFunc = globalEnv->GetPromiseFunction();

    JSHandle<JSPromise> promiseObject =
        JSHandle<JSPromise>::Cast(factory->NewJSObjectByConstructor(JSHandle<JSFunction>(promiseFunc), promiseFunc));
    promiseObject->SetPromiseState(thread, JSTaggedValue(static_cast<int32_t>(PromiseStatus::PENDING)));
    // 2. create asyncfuncobj
    JSHandle<JSAsyncFuncObject> asyncFuncObj = factory->NewJSAsyncFuncObject();
    asyncFuncObj->SetPromise(thread, promiseObject);

    JSHandle<GeneratorContext> context = factory->NewGeneratorContext();
    context->SetGeneratorObject(thread, asyncFuncObj);

    // change state to EXECUTING
    asyncFuncObj->SetState(thread, JSGeneratorState::EXECUTING);
    asyncFuncObj->SetGeneratorContext(thread, context);

    // 3. return asyncfuncobj
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return asyncFuncObj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::ToNumber(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, Tonumber);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> number(thread, value);
    // may return exception
    return JSTaggedValue::ToNumeric(thread, number).GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::NotDyn(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, NotDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> inputTag(thread, value);
    JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, inputTag);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    if (inputVal->IsBigInt()) {
        JSHandle<BigInt> bigValue(inputVal);
        return BigInt::BitwiseNOT(thread, bigValue).GetTaggedValue();
    }
    int32_t number = JSTaggedValue::ToInt32(thread, inputVal);
    return JSTaggedValue(~number);  // NOLINT(hicpp-signed-bitwise)
}

JSTaggedValue SlowRuntimeStub::IncDyn(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, IncDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> inputTag(thread, value);
    JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, inputTag);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    if (inputVal->IsBigInt()) {
        JSHandle<BigInt> bigValue(inputVal);
        return BigInt::BigIntAddOne(thread, bigValue).GetTaggedValue();
    }
    JSTaggedNumber number(inputVal.GetTaggedValue());
    return (++number);
}

JSTaggedValue SlowRuntimeStub::DecDyn(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, DecDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> inputTag(thread, value);
    JSHandle<JSTaggedValue> inputVal = JSTaggedValue::ToNumeric(thread, inputTag);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    if (inputVal->IsBigInt()) {
        JSHandle<BigInt> bigValue(inputVal);
        return BigInt::BigIntSubOne(thread, bigValue).GetTaggedValue();
    }
    JSTaggedNumber number(inputVal.GetTaggedValue());
    return (--number);
}

void SlowRuntimeStub::ThrowDyn(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ThrowDyn);

    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> obj(thread, value);
    JSHandle<ObjectWrapper> wrapperObject = factory->NewObjectWrapper(obj);

    thread->SetException(wrapperObject.GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::GetPropIterator(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, GetPropIterator);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, value);
    JSHandle<JSForInIterator> iteratorHandle = JSObject::EnumerateObjectProperties(thread, objHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    iteratorHandle->SetFastRemainingIndex(JSTaggedNumber(0));
    return iteratorHandle.GetTaggedValue();
}

void SlowRuntimeStub::ThrowConstAssignment(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ThrowConstAssignment);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    JSHandle<EcmaString> name(thread, value.GetTaggedObject());
    JSHandle<EcmaString> info = factory->NewFromCanBeCompressString("Assignment to const variable ");

    JSHandle<EcmaString> msg = factory->ConcatFromString(info, name);
    THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR, msg).GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::Add2Dyn(JSThread *thread, EcmaVM *ecmaVm, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, Add2Dyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    if (leftValue->IsString() && rightValue->IsString()) {
        EcmaString *newString =
            EcmaString::Concat(JSHandle<EcmaString>(leftValue), JSHandle<EcmaString>(rightValue), ecmaVm);
        return JSTaggedValue(newString);
    }
    JSHandle<JSTaggedValue> primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, leftValue));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> primitiveA1(thread, JSTaggedValue::ToPrimitive(thread, rightValue));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    // contain string
    if (primitiveA0->IsString() || primitiveA1->IsString()) {
        JSHandle<EcmaString> stringA0 = JSTaggedValue::ToString(thread, primitiveA0);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        JSHandle<EcmaString> stringA1 = JSTaggedValue::ToString(thread, primitiveA1);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        EcmaString *newString = EcmaString::Concat(stringA0, stringA1, ecmaVm);
        return JSTaggedValue(newString);
    }

    JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, primitiveA0);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, primitiveA1);

    if (valLeft->IsBigInt() || valRight->IsBigInt()) {
        if (valLeft->IsBigInt() && valRight->IsBigInt()) {
            JSHandle<BigInt> bigLeft(valLeft);
            JSHandle<BigInt> bigRight(valRight);
            return BigInt::Add(thread, bigLeft, bigRight).GetTaggedValue();
        }
        return ThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
    }

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    double a0Double = valLeft->GetNumber();
    double a1Double = valRight->GetNumber();
    return JSTaggedValue(a0Double + a1Double);
}

JSTaggedValue SlowRuntimeStub::Sub2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, Sub2Dyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftHandle(thread, left);
    JSHandle<JSTaggedValue> rightHandle(thread, right);

    JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, leftHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, rightHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    if (valLeft->IsBigInt() || valRight->IsBigInt()) {
        if (valLeft->IsBigInt() && valRight->IsBigInt()) {
            JSHandle<BigInt> bigLeft(valLeft);
            JSHandle<BigInt> bigRight(valRight);
            return BigInt::Subtract(thread, bigLeft, bigRight).GetTaggedValue();
        }
        return ThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
    }

    JSTaggedNumber number0(valLeft.GetTaggedValue());
    JSTaggedNumber number1(valRight.GetTaggedValue());
    return number0 - number1;
}

JSTaggedValue SlowRuntimeStub::Mul2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, Mul2Dyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);

    // 6. Let lnum be ToNumeric(leftValue).
    JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, leftValue);
    // 7. ReturnIfAbrupt(lnum).
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    // 8. Let rnum be ToNumeric(rightValue).
    JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, rightValue);
    // 9. ReturnIfAbrupt(rnum).
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    if (valLeft->IsBigInt() || valRight->IsBigInt()) {
        if (valLeft->IsBigInt() && valRight->IsBigInt()) {
            JSHandle<BigInt> bigLeft(valLeft);
            JSHandle<BigInt> bigRight(valRight);
            return BigInt::Multiply(thread, bigLeft, bigRight).GetTaggedValue();
        }
        return ThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
    }

    // 12.6.3.1 Applying the * Operator
    JSTaggedNumber number0(valLeft.GetTaggedValue());
    JSTaggedNumber number1(valRight.GetTaggedValue());
    return number0 * number1;
}

JSTaggedValue SlowRuntimeStub::Div2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, Div2Dyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftHandle(thread, left);
    JSHandle<JSTaggedValue> rightHandle(thread, right);

    JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, leftHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, rightHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    if (valLeft->IsBigInt() || valRight->IsBigInt()) {
        if (valLeft->IsBigInt() && valRight->IsBigInt()) {
            JSHandle<BigInt> bigLeft(valLeft);
            JSHandle<BigInt> bigRight(valRight);
            return BigInt::Divide(thread, bigLeft, bigRight).GetTaggedValue();
        }
        return ThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
    }

    double dLeft = valLeft->GetNumber();
    double dRight = valRight->GetNumber();

    if (dRight == 0) {
        if (dLeft == 0 || std::isnan(dLeft)) {
            return JSTaggedValue(base::NAN_VALUE);
        }
        bool positive = (((bit_cast<uint64_t>(dRight)) & base::DOUBLE_SIGN_MASK) ==
                         ((bit_cast<uint64_t>(dLeft)) & base::DOUBLE_SIGN_MASK));
        return JSTaggedValue(positive ? base::POSITIVE_INFINITY : -base::POSITIVE_INFINITY);
    }
    return JSTaggedValue(dLeft / dRight);
}

JSTaggedValue SlowRuntimeStub::Mod2Dyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, Mod2Dyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftHandle(thread, left);
    JSHandle<JSTaggedValue> rightHandle(thread, right);

    JSHandle<JSTaggedValue> valLeft = JSTaggedValue::ToNumeric(thread, leftHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> valRight = JSTaggedValue::ToNumeric(thread, rightHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    if (valLeft->IsBigInt() || valRight->IsBigInt()) {
        if (valLeft->IsBigInt() && valRight->IsBigInt()) {
            JSHandle<BigInt> leftBigint(valLeft);
            JSHandle<BigInt> rightBigint(valRight);
            return BigInt::Remainder(thread, leftBigint, rightBigint).GetTaggedValue();
        }
        return ThrowTypeError(thread, "Cannot mix BigInt and other types, use explicit conversions");
    }

    double dLeft = valLeft->GetNumber();
    double dRight = valRight->GetNumber();

    // 12.6.3.3 Applying the % Operator
    if ((dRight == 0.0) || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) {
        return JSTaggedValue(base::NAN_VALUE);
    }
    if ((dLeft == 0.0) || std::isinf(dRight)) {
        return JSTaggedValue(dLeft);
    }

    return JSTaggedValue(std::fmod(dLeft, dRight));
}

JSTaggedValue SlowRuntimeStub::EqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, EqDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    bool ret = JSTaggedValue::Equal(thread, leftValue, rightValue);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}

JSTaggedValue SlowRuntimeStub::NotEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, NotEqDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    bool ret = JSTaggedValue::Equal(thread, leftValue, rightValue);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return (ret ? JSTaggedValue::False() : JSTaggedValue::True());
}

JSTaggedValue SlowRuntimeStub::LessDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, LessDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) == ComparisonResult::LESS;
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}

JSTaggedValue SlowRuntimeStub::LessEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, LessEqDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) <= ComparisonResult::EQUAL;
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}

JSTaggedValue SlowRuntimeStub::GreaterDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, GreaterDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    bool ret = JSTaggedValue::Compare(thread, leftValue, rightValue) == ComparisonResult::GREAT;
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}

JSTaggedValue SlowRuntimeStub::GreaterEqDyn(JSThread *thread, JSTaggedValue left, JSTaggedValue right)
{
    INTERPRETER_TRACE(thread, GreaterEqDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> leftValue(thread, left);
    JSHandle<JSTaggedValue> rightValue(thread, right);
    ComparisonResult comparison = JSTaggedValue::Compare(thread, leftValue, rightValue);
    bool ret = (comparison == ComparisonResult::GREAT) || (comparison == ComparisonResult::EQUAL);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return (ret ? JSTaggedValue::True() : JSTaggedValue::False());
}

JSTaggedValue SlowRuntimeStub::ToJSTaggedValueWithInt32(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ToJSTaggedValueWithInt32);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    int32_t res = JSTaggedValue::ToInt32(thread, valueHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue(res);
}

JSTaggedValue SlowRuntimeStub::ToJSTaggedValueWithUint32(JSThread *thread, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ToJSTaggedValueWithUint32);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    int32_t res = JSTaggedValue::ToUint32(thread, valueHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue(res);
}

JSTaggedValue SlowRuntimeStub::DelObjProp(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop)
{
    INTERPRETER_TRACE(thread, Delobjprop);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> jsObj(JSTaggedValue::ToObject(thread, objHandle));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, propHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    bool ret = JSTaggedValue::DeletePropertyOrThrow(thread, jsObj, propKey);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue(ret);
}

JSTaggedValue SlowRuntimeStub::NewObjDynRange(JSThread *thread, uint16_t argsCount, JSTaggedValue func,
                                              JSTaggedValue newTarget, JSTaggedType *stkargs)
{
    INTERPRETER_TRACE(thread, NewobjDynrange);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> funcHandle(thread, JSTaggedValue(func));
    JSHandle<JSTaggedValue> newTargetHandle(thread, JSTaggedValue(newTarget));

    JSHandle<JSTaggedValue> preArgs(thread, JSTaggedValue::Undefined());
    auto tagged = SlowRuntimeHelper::Construct(thread, funcHandle, newTargetHandle, preArgs, argsCount, stkargs);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return tagged;
}

JSTaggedValue SlowRuntimeStub::CreateObjectWithExcludedKeys(JSThread *thread, uint16_t numKeys, JSTaggedValue objVal,
                                                            JSTaggedType *stkargs)
{
    INTERPRETER_TRACE(thread, CreateObjectWithExcludedKeys);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSTaggedValue> objTval(thread, objVal);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    JSHandle<JSTaggedValue> fromValue(thread, objVal);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    // 3. If source is undefined or null, return target.
    if (fromValue->IsNull() || fromValue->IsUndefined()) {
        return objVal;
    }
    // 4. Let from be ! ToObject(source).
    JSHandle<JSObject> from = JSTaggedValue::ToObject(thread, fromValue);
    ASSERT_NO_ABRUPT_COMPLETION(thread);

    // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    JSTaggedValue vreg(stkargs[0]);
    ArraySizeT numExcludedKeys = vreg.IsUndefined() ? 0 : numKeys + 1;
    // 5.Let keys be obj.[[OwnPropertyKeys]]().
    JSHandle<TaggedArray> fromKeys = JSTaggedValue::GetOwnPropertyKeys(thread, JSHandle<JSTaggedValue>::Cast(from));
    // 6. For each element nextKey of keys, do
    JSHandle<JSObject> newObj = factory->NewEmptyJSObject();

    bool excluded = true;
    for (ArraySizeT i = 0; i < fromKeys->GetLength(); i++) {
        JSMutableHandle<JSTaggedValue> key(thread, fromKeys->Get(i));
        // a. Let excluded be false.
        excluded = false;
        // b. For each element e of excludedItems, do
        for (ArraySizeT e = 0; e < numExcludedKeys; e++) {
            // i. If SameValue(e, nextKey) is true, then
            // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
            JSTaggedValue ereg(stkargs[e]);
            if (JSTaggedValue::SameValue(key, JSHandle<JSTaggedValue>(thread, ereg))) {
                // 1. Set excluded to true.
                excluded = true;
                break;
            }
        }
        // c. If excluded is false, then
        if (!excluded) {
            // i. Let desc be ? from.[[GetOwnProperty]](nextKey).
            PropertyDescriptor desc(thread);
            bool status = JSTaggedValue::GetOwnProperty(thread, JSHandle<JSTaggedValue>(from), key, desc);
            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
            // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
            if (status && desc.IsEnumerable()) {
                JSHandle<JSTaggedValue> propValue =
                    JSTaggedValue::GetProperty(thread, JSHandle<JSTaggedValue>(from), key).GetValue();
                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
                JSObject::CreateDataPropertyOrThrow(thread, newObj, key, propValue);
                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
            }
        }
    }
    return newObj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::ExpDyn(JSThread *thread, JSTaggedValue base, JSTaggedValue exponent)
{
    INTERPRETER_TRACE(thread, ExpDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> baseTag(thread, base);
    JSHandle<JSTaggedValue> exponentTag(thread, exponent);
    JSHandle<JSTaggedValue> valBase = JSTaggedValue::ToNumeric(thread, baseTag);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSTaggedValue> valExponent = JSTaggedValue::ToNumeric(thread, exponentTag);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    if (valBase->IsBigInt() || valExponent->IsBigInt()) {
        if (valBase->IsBigInt() && valExponent->IsBigInt()) {
            JSHandle<BigInt> bigBaseValue(valBase);
            JSHandle<BigInt> bigExponentValue(valExponent);
            return BigInt::Exponentiate(thread, bigBaseValue, bigExponentValue).GetTaggedValue();
        }
        THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot mix BigInt and other types, use explicit conversions",
                                    JSTaggedValue::Exception());
    }

    return base::NumberHelper::Pow(baseTag->GetNumber(), exponent.GetNumber());
}

JSTaggedValue SlowRuntimeStub::IsInDyn(JSThread *thread, JSTaggedValue prop, JSTaggedValue obj)
{
    INTERPRETER_TRACE(thread, IsInDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    if (!objHandle->IsECMAObject()) {
        return ThrowTypeError(thread, "Cannot use 'in' operator in Non-Object");
    }
    JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, propHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    bool ret = JSTaggedValue::HasProperty(thread, objHandle, propKey);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue(ret);
}

JSTaggedValue SlowRuntimeStub::InstanceofDyn(JSThread *thread, JSTaggedValue obj, JSTaggedValue target)
{
    INTERPRETER_TRACE(thread, InstanceofDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> targetHandle(thread, target);
    bool ret = JSObject::InstanceOf(thread, objHandle, targetHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue(ret);
}

JSTaggedValue SlowRuntimeStub::NewLexicalEnvDyn(JSThread *thread, uint16_t numVars)
{
    INTERPRETER_TRACE(thread, NewlexenvDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<LexicalEnv> newEnv = factory->NewLexicalEnv(numVars);

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return newEnv.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::CreateIterResultObj(JSThread *thread, JSTaggedValue value, bool done)
{
    INTERPRETER_TRACE(thread, CreateIterResultObj);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> valueHandle(thread, value);
    JSHandle<JSObject> iter = JSIterator::CreateIterResultObject(thread, valueHandle, done);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return iter.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::CreateGeneratorObj(JSThread *thread, JSTaggedValue genFunc)
{
    INTERPRETER_TRACE(thread, CreateGeneratorObj);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> generatorFunction(thread, genFunc);
    JSHandle<JSGeneratorObject> obj = factory->NewJSGeneratorObject(generatorFunction);
    JSHandle<GeneratorContext> context = factory->NewGeneratorContext();
    context->SetGeneratorObject(thread, obj.GetTaggedValue());

    // change state to SUSPENDED_START
    obj->SetState(thread, JSGeneratorState::SUSPENDED_START);
    obj->SetGeneratorContext(thread, context);

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return obj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::SetGeneratorState(JSThread *thread, JSTaggedValue genObj, uint8_t state)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSGeneratorObject> generatorObjectHandle(thread, genObj);
    generatorObjectHandle->SetState(thread, static_cast<JSGeneratorState>(state));

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::Undefined();
}

JSTaggedValue SlowRuntimeStub::CreateAsyncGeneratorObj(JSThread *thread, JSTaggedValue genFunc)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSTaggedValue> generatorFunction(thread, genFunc);
    JSHandle<JSAsyncGeneratorObject> obj = factory->NewJSAsyncGeneratorObject(generatorFunction);
    JSHandle<GeneratorContext> context = factory->NewGeneratorContext();
    JSHandle<TaggedQueue> asyncQueue = factory->NewTaggedQueue(0);
    context->SetGeneratorObject(thread, obj.GetTaggedValue());

    // change state to SUSPENDED_START
    obj->SetState(thread, JSGeneratorState::SUSPENDED_START);
    obj->SetGeneratorContext(thread, context);
    obj->SetAsyncGeneratorQueue(thread, asyncQueue);

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return obj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::SuspendGenerator(JSThread *thread, JSTaggedValue genObj, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, SuspendGenerator);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSGeneratorObject> generatorObjectHandle(thread, genObj);
    JSHandle<GeneratorContext> genContextHandle(thread, generatorObjectHandle->GetGeneratorContext());
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    // save stack, should copy cur_frame, function execute over will free cur_frame
    SlowRuntimeHelper::SaveFrameToContext(thread, genContextHandle);

    return valueHandle.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::SuspendAsyncGenerator(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSGeneratorObject> asyncGeneratorObjectHandle(thread, asyncGenObj);
    JSHandle<JSTaggedValue> asyncGeneratorHandle(thread, asyncGenObj);
    JSHandle<GeneratorContext> genContextHandle(thread, asyncGeneratorObjectHandle->GetGeneratorContext());
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    // save stack, should copy cur_frame, function execute over will free cur_frame
    SlowRuntimeHelper::SaveFrameToContext(thread, genContextHandle);

    return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorHandle, valueHandle, false)
        .GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::AsyncFunctionAwait(JSThread *thread, JSTaggedValue asyncFuncObj, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, AsyncFunctionAwait);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSAsyncFuncObject> asyncFuncObjHandle(thread, asyncFuncObj);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    JSAsyncFuncObject::AsyncFunctionAwait(thread, asyncFuncObjHandle, valueHandle);
    JSHandle<JSPromise> promise(thread, asyncFuncObjHandle->GetPromise());

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return promise.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::AsyncFunctionResolveOrReject(JSThread *thread, JSTaggedValue asyncFuncObj,
                                                            JSTaggedValue value, bool isResolve)
{
    if (isResolve) {
        INTERPRETER_TRACE(thread, AsyncFunctionResolve);
    } else {
        INTERPRETER_TRACE(thread, AsyncFunctionReject);
    }

    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSAsyncFuncObject> asyncFuncObjHandle(thread, asyncFuncObj);
    JSHandle<JSPromise> promise(thread, asyncFuncObjHandle->GetPromise());
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    // ActivePromise
    JSHandle<ResolvingFunctionsRecord> reactions = JSPromise::CreateResolvingFunctions(thread, promise);
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    JSHandle<JSTaggedValue> thisArg = globalConst->GetHandledUndefined();
    JSHandle<JSTaggedValue> activeFunc;
    if (isResolve) {
        activeFunc = JSHandle<JSTaggedValue>(thread, reactions->GetResolveFunction());
    } else {
        activeFunc = JSHandle<JSTaggedValue>(thread, reactions->GetRejectFunction());
    }
    auto info = NewRuntimeCallInfo(thread, activeFunc, thisArg, JSTaggedValue::Undefined(), 1);
    info->SetCallArgs(valueHandle);
    [[maybe_unused]] JSTaggedValue res = JSFunction::Call(info.Get());

    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return promise.GetTaggedValue();
}

// 27.6.3.2 AsyncGeneratorStart (generator, generatorBody)
JSTaggedValue SlowRuntimeStub::AsyncGeneratorResolve(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> asyncGeneratorHandle(thread, asyncGenObj);
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    // e. Set generator.[[AsyncGeneratorState]] to completed.
    JSAsyncGeneratorObject::Cast(asyncGenObj.GetHeapObject())->SetState(thread, JSGeneratorState::COMPLETED);

    // f. If result is a normal completion, let resultValue be undefined.
    // i. Let resultValue be result.[[Value]].
    // h. Return ! AsyncGeneratorResolve(generator, resultValue, true).
    return JSAsyncGeneratorObject::AsyncGeneratorResolve(thread, asyncGeneratorHandle, valueHandle, true)
        .GetTaggedValue();
}

// 27.6.3.2 AsyncGeneratorStart (generator, generatorBody)
JSTaggedValue SlowRuntimeStub::AsyncGeneratorReject(JSThread *thread, JSTaggedValue asyncGenObj, JSTaggedValue value)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> asyncGeneratorHandle(thread, asyncGenObj);
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    // e. Set generator.[[AsyncGeneratorState]] to completed.
    JSAsyncGeneratorObject::Cast(asyncGenObj.GetHeapObject())->SetState(thread, JSGeneratorState::COMPLETED);

    // ii. ii. If result.[[Type]] is not return, then
    // 1. Return ! AsyncGeneratorReject(generator, resultValue).
    // 1. 1. Return ! AsyncGeneratorReject(generator, resultValue).
    return JSAsyncGeneratorObject::AsyncGeneratorReject(thread, asyncGeneratorHandle, valueHandle).GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::NewObjSpreadDyn(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget,
                                               JSTaggedValue array)
{
    INTERPRETER_TRACE(thread, NewobjspreadDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> jsArray(thread, array);
    if (!jsArray->IsJSArray()) {
        return ThrowTypeError(thread, "Cannot Newobjspread");
    }

    uint32_t length = JSHandle<JSArray>::Cast(jsArray)->GetArrayLength();
    auto info = NewRuntimeCallInfo(thread, func, JSTaggedValue::Undefined(), newTarget, length);
    for (uint32_t i = 0; i < length; ++i) {
        auto prop = JSTaggedValue::GetProperty(thread, jsArray, i).GetValue();
        info->SetCallArg(i, prop.GetTaggedValue());
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    }
    return SlowRuntimeHelper::NewObject(info.Get());
}

void SlowRuntimeStub::ThrowTdz(JSThread *thread, JSTaggedValue bindingName)
{
    INTERPRETER_TRACE(thread, ThrowTdz);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> name(thread, bindingName);
    JSHandle<EcmaString> info = factory->NewFromCanBeCompressString(" is not initialized");

    JSHandle<EcmaString> msg = factory->ConcatFromString(name, info);
    THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg).GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::ThrowIfSuperNotCorrectCall(JSThread *thread, uint16_t index, JSTaggedValue thisValue)
{
    INTERPRETER_TRACE(thread, ThrowIfSuperNotCorrectCall);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    if (index == 0 && (thisValue.IsUndefined() || thisValue.IsHole())) {
        return ThrowReferenceError(thread, JSTaggedValue::Undefined(), "sub-class must call super before use 'this'");
    }
    if (index == 1 && !thisValue.IsUndefined() && !thisValue.IsHole()) {
        return ThrowReferenceError(thread, JSTaggedValue::Undefined(), "super() forbidden re-bind 'this'");
    }
    return JSTaggedValue::True();
}

void SlowRuntimeStub::ThrowIfNotObject(JSThread *thread)
{
    INTERPRETER_TRACE(thread, ThrowIfNotObject);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    THROW_TYPE_ERROR(thread, "Inner return result is not object");
}

void SlowRuntimeStub::ThrowThrowNotExists(JSThread *thread)
{
    INTERPRETER_TRACE(thread, ThrowThrowNotExists);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    THROW_TYPE_ERROR(thread, "Throw method is not defined");
}

void SlowRuntimeStub::ThrowPatternNonCoercible(JSThread *thread)
{
    INTERPRETER_TRACE(thread, ThrowPatternNonCoercible);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<EcmaString> msg(thread->GlobalConstants()->GetHandledObjNotCoercibleString());
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    THROW_NEW_ERROR_AND_RETURN(thread, factory->NewJSError(base::ErrorType::TYPE_ERROR, msg).GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::StOwnByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StOwnByNameDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    const GlobalEnvConstants *globalConst = thread->GlobalConstants();
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    ASSERT(propHandle->IsStringOrSymbol());

    if (objHandle->IsClassConstructor() &&
        JSTaggedValue::SameValue(propHandle, globalConst->GetHandledPrototypeString())) {
        return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError");
    }

    // property in class is non-enumerable
    bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor());

    PropertyDescriptor desc(thread, valueHandle, true, enumerable, true);
    bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc);
    if (!ret) {
        return ThrowTypeError(thread, "SetOwnByName failed");
    }
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::StOwnByNameWithNameSet(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop,
                                                      JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StOwnByNameDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    ASSERT(propHandle->IsStringOrSymbol());

    JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, propHandle);

    // property in class is non-enumerable
    bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor());

    PropertyDescriptor desc(thread, valueHandle, true, enumerable, true);
    bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc);
    if (!ret) {
        return ThrowTypeError(thread, "SetOwnByNameWithNameSet failed");
    }
    JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(valueHandle), propKey,
                                    JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::StOwnByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StOwnByIdDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> idxHandle(thread, JSTaggedValue(idx));
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    // property in class is non-enumerable
    bool enumerable = !(objHandle->IsClassPrototype() || objHandle->IsClassConstructor());

    PropertyDescriptor desc(thread, valueHandle, true, enumerable, true);
    bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, idxHandle, desc);
    if (!ret) {
        return ThrowTypeError(thread, "SetOwnByIndex failed");
    }
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::StOwnByValue(JSThread *thread, const JSHandle<JSTaggedValue> &obj,
                                            const JSHandle<JSTaggedValue> &key, const JSHandle<JSTaggedValue> &value)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    INTERPRETER_TRACE(thread, StOwnByValueDyn);
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();

    if (obj->IsClassConstructor() && JSTaggedValue::SameValue(key, globalConst->GetHandledPrototypeString())) {
        return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError");
    }

    // propery in class is non-enumerable
    bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor());

    PropertyDescriptor desc(thread, value, true, enumerable, true);
    JSMutableHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, key));
    bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc);
    if (!ret) {
        return ThrowTypeError(thread, "StOwnByValue failed");
    }
    if (value->IsJSFunction()) {
        if (propKey->IsNumber()) {
            propKey.Update(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()).GetTaggedValue());
        }
        JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(value), propKey,
                                        JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    }
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::StOwnByValueWithNameSet(JSThread *thread, JSHandle<JSTaggedValue> obj,
                                                       JSHandle<JSTaggedValue> key, JSHandle<JSTaggedValue> value)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    INTERPRETER_TRACE(thread, StOwnByValueDyn);
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();

    if (obj->IsClassConstructor() && JSTaggedValue::SameValue(key, globalConst->GetHandledPrototypeString())) {
        return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError");
    }

    // property in class is non-enumerable
    bool enumerable = !(obj->IsClassPrototype() || obj->IsClassConstructor());

    PropertyDescriptor desc(thread, value, true, enumerable, true);
    JSMutableHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, key));
    bool ret = JSTaggedValue::DefineOwnProperty(thread, obj, propKey, desc);
    if (!ret) {
        return ThrowTypeError(thread, "StOwnByValueWithNameSet failed");
    }
    if (value->IsJSFunction()) {
        if (propKey->IsNumber()) {
            propKey.Update(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()).GetTaggedValue());
        }
        JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(value), propKey,
                                        JSHandle<JSTaggedValue>(thread, JSTaggedValue::Undefined()));
    }
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::CreateEmptyArray(JSThread *thread, ObjectFactory *factory, JSHandle<GlobalEnv> globalEnv)
{
    INTERPRETER_TRACE(thread, CreateEmptyArray);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSFunction> builtinObj(globalEnv->GetArrayFunction());
    JSHandle<JSObject> arr = factory->NewJSObjectByConstructor(builtinObj, JSHandle<JSTaggedValue>(builtinObj));
    return arr.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::CreateEmptyObject(JSThread *thread, ObjectFactory *factory,
                                                 JSHandle<GlobalEnv> globalEnv)
{
    INTERPRETER_TRACE(thread, CreateEmptyObject);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSFunction> builtinObj(globalEnv->GetObjectFunction());
    JSHandle<JSObject> obj = factory->NewJSObjectByConstructor(builtinObj, JSHandle<JSTaggedValue>(builtinObj));
    return obj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::CreateObjectWithBuffer(JSThread *thread, ObjectFactory *factory, JSObject *literal)
{
    INTERPRETER_TRACE(thread, CreateObjectWithBuffer);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSObject> obj(thread, literal);
    JSHandle<JSObject> objLiteral = factory->CloneObjectLiteral(obj);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    return objLiteral.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::CreateObjectHavingMethod(JSThread *thread, ObjectFactory *factory, JSObject *literal,
                                                        JSTaggedValue env, ConstantPool *constpool)
{
    INTERPRETER_TRACE(thread, CreateObjectHavingMethod);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSObject> obj(thread, literal);
    JSHandle<JSObject> objLiteral = factory->CloneObjectLiteral(
        obj, JSHandle<JSTaggedValue>(thread, env), JSHandle<JSTaggedValue>(thread, JSTaggedValue(constpool)));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    return objLiteral.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::SetObjectWithProto(JSThread *thread, JSTaggedValue proto, JSTaggedValue obj)
{
    INTERPRETER_TRACE(thread, SetObjectWithProto);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    if (!proto.IsECMAObject() && !proto.IsNull()) {
        return JSTaggedValue::False();
    }
    JSHandle<JSTaggedValue> protoHandle(thread, proto);
    JSHandle<JSObject> objHandle(thread, obj);
    JSObject::SetPrototype(thread, objHandle, protoHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::CloseIterator(JSThread *thread, JSTaggedValue iter)
{
    INTERPRETER_TRACE(thread, CloseIterator);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();

    JSHandle<JSTaggedValue> iterHandle(thread, iter);
    JSHandle<JSTaggedValue> record;
    if (UNLIKELY(thread->HasPendingException())) {
        JSHandle<JSTaggedValue> exception(thread,
                                          ObjectWrapper::Cast(thread->GetException().GetTaggedObject())->GetValue());
        record = JSHandle<JSTaggedValue>(factory->NewCompletionRecord(CompletionRecord::THROW, exception));
    } else {
        JSHandle<JSTaggedValue> undefinedVal = globalConst->GetHandledUndefined();
        record = JSHandle<JSTaggedValue>(factory->NewCompletionRecord(CompletionRecord::NORMAL, undefinedVal));
    }
    JSHandle<JSTaggedValue> result = JSIterator::IteratorClose(thread, iterHandle, record);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return result.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::ImportModule([[maybe_unused]] JSThread *thread,
                                            [[maybe_unused]] JSTaggedValue moduleName)
{
    INTERPRETER_TRACE(thread, ImportModule);
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<JSTaggedValue> name(thread, moduleName);
    JSHandle<JSTaggedValue> module = thread->GetEcmaVM()->GetModuleByName(name);
    return module.GetTaggedValue();  // return moduleRef
}

void SlowRuntimeStub::StModuleVar([[maybe_unused]] JSThread *thread, [[maybe_unused]] JSTaggedValue exportName,
                                  [[maybe_unused]] JSTaggedValue exportObj)
{
    INTERPRETER_TRACE(thread, StModuleVar);
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<JSTaggedValue> name(thread, exportName);
    JSHandle<JSTaggedValue> value(thread, exportObj);
    thread->GetEcmaVM()->GetModuleManager()->AddModuleItem(thread, name, value);
}

void SlowRuntimeStub::CopyModule(JSThread *thread, JSTaggedValue srcModule)
{
    INTERPRETER_TRACE(thread, CopyModule);
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<JSTaggedValue> srcModuleObj(thread, srcModule);
    thread->GetEcmaVM()->GetModuleManager()->CopyModule(thread, srcModuleObj);
}

JSTaggedValue SlowRuntimeStub::LdModvarByName([[maybe_unused]] JSThread *thread,
                                              [[maybe_unused]] JSTaggedValue moduleObj,
                                              [[maybe_unused]] JSTaggedValue itemName)
{
    INTERPRETER_TRACE(thread, LdModvarByName);
    [[maybe_unused]] EcmaHandleScope scope(thread);
    JSHandle<JSTaggedValue> module(thread, moduleObj);
    JSHandle<JSTaggedValue> item(thread, itemName);
    JSHandle<JSTaggedValue> moduleVar = thread->GetEcmaVM()->GetModuleManager()->GetModuleItem(thread, module, item);
    return moduleVar.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::ClassFieldAdd(JSThread *thread, JSTaggedValue obj, JSTaggedValue propName,
                                             JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ClassFieldAdd);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    const GlobalEnvConstants *globalConsts = thread->GlobalConstants();
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, propName);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    ASSERT(propHandle->IsStringOrSymbol());

    if (objHandle->IsClassConstructor() &&
        JSTaggedValue::SameValue(propHandle, globalConsts->GetHandledPrototypeString())) {
        return ThrowTypeError(thread, "In a class, static property named 'prototype' throw a TypeError");
    }

    PropertyDescriptor desc(thread, valueHandle, true, true, true);
    bool ret = JSTaggedValue::DefineOwnProperty(thread, objHandle, propHandle, desc);
    if (!ret) {
        return ThrowTypeError(thread, "ClassFieldAdd failed");
    }
    return JSTaggedValue::True();
}

using PrivateFieldKind = JSConstructorFunction::PrivateFieldKind;

void SlowRuntimeStub::DefineClassPrivateFields(JSThread *thread, ConstantPool *constpool, JSTaggedValue env,
                                               JSTaggedValue ctor, JSTaggedValue privateBuf)
{
    INTERPRETER_TRACE(thread, DefineClassPrivateFields);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    auto factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<TaggedArray> descTemplate(thread, privateBuf);
    JSHandle<ConstantPool> constpoolHandle(thread, constpool);
    JSHandle<JSTaggedValue> lexicalEnv(thread, env);
    JSHandle<JSTaggedValue> ctorHandle(thread, ctor);
    JSHandle<TaggedArray> newDesc = factory->NewTaggedArray(descTemplate->GetLength());
    JSHandle<LinkedHashMap> accessorKeys = LinkedHashMap::Create(thread);

    for (ArraySizeT i = 0; i < descTemplate->GetLength();) {
        JSHandle<JSTaggedValue> kindValue(thread, descTemplate->Get(thread, i));
        auto kind = static_cast<PrivateFieldKind>(kindValue->GetInt());
        newDesc->Set(thread, i++, kindValue);

        switch (kind) {
            case PrivateFieldKind::FIELD:
            case PrivateFieldKind::STATIC_FIELD:
            case PrivateFieldKind::METHOD:
            case PrivateFieldKind::STATIC_METHOD: {
                JSHandle<JSTaggedValue> name(thread, descTemplate->Get(thread, i));
                JSHandle<JSSymbol> privateKey = factory->NewPrivateNameSymbol(name);
                newDesc->Set(thread, i++, privateKey.GetTaggedValue());
                break;
            }
            default: {
                JSHandle<JSTaggedValue> name(thread, descTemplate->Get(thread, i));
                int hash = LinkedHash::Hash(name.GetTaggedValue());
                JSTaggedValue privateKey = accessorKeys->Get(name.GetTaggedValue(), hash);

                if (privateKey.IsUndefined()) {
                    JSHandle<JSSymbol> sharedPrivateKey = factory->NewPrivateNameSymbol(name);
                    LinkedHashMap::Set(thread, accessorKeys, name, JSHandle<JSTaggedValue>::Cast(sharedPrivateKey));
                    privateKey = sharedPrivateKey.GetTaggedValue();
                }

                newDesc->Set(thread, i++, privateKey);
                break;
            }
        }

        switch (kind) {
            case PrivateFieldKind::FIELD:
            case PrivateFieldKind::STATIC_FIELD: {
                break;
            }
            default: {
                JSHandle<JSFunction> valueHandle(thread, descTemplate->Get(thread, i));
                JSHandle<JSFunction> newFunc = factory->CloneJSFuction(valueHandle, valueHandle->GetFunctionKind());
                newFunc->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue());
                newFunc->SetConstantPool(thread, constpoolHandle.GetTaggedValue());
                newDesc->Set(thread, i++, newFunc.GetTaggedValue());
                break;
            }
        }
    }

    JSConstructorFunction::Cast(ctorHandle->GetHeapObject())->SetPrivateFields(newDesc.GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::ClassPrivateMethodOrAccessorAdd(JSThread *thread, JSTaggedValue ctor, JSTaggedValue obj)
{
    INTERPRETER_TRACE(thread, ClassPrivateMethodOrAccessorAdd);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    auto factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSConstructorFunction> ctorHandle(thread, ctor);
    JSHandle<JSObject> objHandle(thread, obj);

    bool isStatic = JSTaggedValue::Equal(thread, JSHandle<JSTaggedValue>::Cast(ctorHandle),
                                         JSHandle<JSTaggedValue>::Cast(objHandle));

    JSHandle<TaggedArray> desc = JSHandle<TaggedArray>(thread, ctorHandle->GetPrivateFields());
    JSHandle<LinkedHashMap> accessorPairs = LinkedHashMap::Create(thread);

    for (ArraySizeT i = 0; i < desc->GetLength();) {
        JSHandle<JSTaggedValue> kindValue(thread, desc->Get(thread, i++));
        auto kind = static_cast<PrivateFieldKind>(kindValue->GetInt());

        switch (kind) {
            case PrivateFieldKind::FIELD:
            case PrivateFieldKind::STATIC_FIELD: {
                i++;
                continue;
            }
            case PrivateFieldKind::METHOD:
            case PrivateFieldKind::GET:
            case PrivateFieldKind::SET: {
                if (isStatic) {
                    i += 2;  // 2: skip key + value
                    continue;
                }
                break;
            }
            case PrivateFieldKind::STATIC_METHOD:
            case PrivateFieldKind::STATIC_GET:
            case PrivateFieldKind::STATIC_SET: {
                if (!isStatic) {
                    i += 2;  // 2: skip key + value
                    continue;
                }
                break;
            }
        }

        JSHandle<JSTaggedValue> privateSymbol(thread, desc->Get(thread, i++));
        JSHandle<JSTaggedValue> value(thread, desc->Get(thread, i++));

        PropertyDescriptor propDesc(thread);
        bool exists = JSObject::GetOwnProperty(thread, objHandle, privateSymbol, propDesc);

        switch (kind) {
            case PrivateFieldKind::METHOD:
            case PrivateFieldKind::STATIC_METHOD: {
                if (exists) {
                    THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot declare same private field twice",
                                                JSTaggedValue::Exception());
                }

                propDesc = PropertyDescriptor(thread, value, false, false, false);
                JSObject::DefineOwnProperty(thread, objHandle, privateSymbol, propDesc);
                continue;
            }
            case PrivateFieldKind::GET:
            case PrivateFieldKind::STATIC_GET: {
                int hash = LinkedHash::Hash(privateSymbol.GetTaggedValue());
                JSTaggedValue pair = accessorPairs->Get(privateSymbol.GetTaggedValue(), hash);

                if (pair.IsUndefined()) {
                    JSHandle<TaggedArray> arr = factory->NewTaggedArray(2);  // 2: getter-setter pair
                    arr->Set(thread, 0, value);
                    arr->Set(thread, 1, JSTaggedValue::Undefined());
                    LinkedHashMap::Set(thread, accessorPairs, privateSymbol, JSHandle<JSTaggedValue>::Cast(arr));
                    continue;
                }
                TaggedArray::Cast(pair.GetHeapObject())->Set(thread, 0, value);
                continue;
            }
            case PrivateFieldKind::SET:
            case PrivateFieldKind::STATIC_SET: {
                int hash = LinkedHash::Hash(privateSymbol.GetTaggedValue());
                JSTaggedValue pair = accessorPairs->Get(privateSymbol.GetTaggedValue(), hash);

                if (pair.IsUndefined()) {
                    JSHandle<TaggedArray> arr = factory->NewTaggedArray(2);  // 2: getter-setter pair
                    arr->Set(thread, 0, JSTaggedValue::Undefined());
                    arr->Set(thread, 1, value);
                    LinkedHashMap::Set(thread, accessorPairs, privateSymbol, JSHandle<JSTaggedValue>::Cast(arr));
                    continue;
                }
                TaggedArray::Cast(pair.GetHeapObject())->Set(thread, 1, value);
                continue;
            }
            default: {
                UNREACHABLE();
                break;
            }
        }
    }

    PropertyDescriptor propDesc(thread);
    for (int i = 0; i < accessorPairs->NumberOfElements(); i++) {
        JSHandle<JSTaggedValue> privateSymbol(thread, accessorPairs->GetKey(i));

        int hash = LinkedHash::Hash(privateSymbol.GetTaggedValue());
        JSHandle<TaggedArray> accessorPair(thread, accessorPairs->Get(privateSymbol.GetTaggedValue(), hash));
        JSHandle<JSTaggedValue> getter(thread, accessorPair->Get(0));
        JSHandle<JSTaggedValue> setter(thread, accessorPair->Get(1));

        propDesc.SetGetter(getter);
        propDesc.SetSetter(setter);
        propDesc.SetConfigurable(false);
        propDesc.SetEnumerable(false);
        JSObject::DefineOwnProperty(thread, objHandle, privateSymbol, propDesc);
    }

    return JSTaggedValue::Undefined();
}

JSTaggedValue SlowRuntimeStub::ClassPrivateFieldAdd(JSThread *thread, JSTaggedValue ctor, JSTaggedValue obj,
                                                    JSTaggedValue propName, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ClassPrivateFieldAdd);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    PropertyDescriptor propDesc(thread);
    [[maybe_unused]] PrivateFieldKind kind;

    JSHandle<JSObject> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propNameHandle(thread, propName);
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    JSHandle<JSTaggedValue> privateSymbol(
        thread, SlowRuntimeHelper::FindPrivateKey(thread, JSHandle<JSConstructorFunction>(thread, ctor), objHandle,
                                                  propNameHandle, propDesc, kind));

    if (!propDesc.GetValue()->IsHole()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot declare same private field twice", JSTaggedValue::Exception());
    }

    propDesc = PropertyDescriptor(thread, valueHandle, true, false, false);
    JSObject::DefineOwnProperty(thread, objHandle, privateSymbol, propDesc);

    return JSTaggedValue::Undefined();
}

JSTaggedValue SlowRuntimeStub::ClassPrivateFieldGet(JSThread *thread, JSTaggedValue ctor, JSTaggedValue obj,
                                                    JSTaggedValue propName)
{
    INTERPRETER_TRACE(thread, ClassPrivateFieldGet);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    PropertyDescriptor propDesc(thread);
    [[maybe_unused]] PrivateFieldKind kind;

    JSHandle<JSConstructorFunction> ctorHandle(thread, ctor);
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propNameHandle(thread, propName);
    JSHandle<JSObject> jsObj(JSTaggedValue::ToObject(thread, objHandle));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    JSHandle<JSTaggedValue> privateSymbol(
        thread, SlowRuntimeHelper::FindPrivateKey(thread, ctorHandle, jsObj, propNameHandle, propDesc, kind));

    if (propDesc.GetValue()->IsHole()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot read private member to an object whose class did not declare it",
                                    JSTaggedValue::Exception());
    }

    if (propDesc.HasValue()) {
        return propDesc.GetValue().GetTaggedValue();
    }

    if (!propDesc.HasGetter()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Private field was defined without a getter", JSTaggedValue::Exception());
    }

    auto info = NewRuntimeCallInfo(thread, propDesc.GetGetter(), objHandle, JSTaggedValue::Undefined(), 0);
    JSTaggedValue res = JSFunction::Call(info.Get());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res;
}

JSTaggedValue SlowRuntimeStub::ClassPrivateFieldSet(JSThread *thread, JSTaggedValue ctor, JSTaggedValue obj,
                                                    JSTaggedValue propName, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, ClassPrivateFieldSet);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    PropertyDescriptor propDesc(thread);
    [[maybe_unused]] PrivateFieldKind kind;

    JSHandle<JSConstructorFunction> ctorHandle(thread, ctor);
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propNameHandle(thread, propName);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    JSHandle<JSObject> jsObj(JSTaggedValue::ToObject(thread, objHandle));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    JSHandle<JSTaggedValue> privateSymbol(
        thread, SlowRuntimeHelper::FindPrivateKey(thread, ctorHandle, jsObj, propNameHandle, propDesc, kind));

    if (propDesc.GetValue()->IsHole()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot read private member to an object whose class did not declare it",
                                    JSTaggedValue::Exception());
    }

    if (propDesc.HasValue()) {
        if (kind == PrivateFieldKind::METHOD || kind == PrivateFieldKind::STATIC_METHOD) {
            THROW_TYPE_ERROR_AND_RETURN(thread, "Private method is not writable", JSTaggedValue::Exception());
        }

        propDesc.SetValue(valueHandle);
        JSObject::DefineOwnProperty(thread, jsObj, privateSymbol, propDesc);
        return JSTaggedValue::Undefined();
    }

    if (!propDesc.HasSetter()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Private field was defined without a setter", JSTaggedValue::Exception());
    }

    auto info = NewRuntimeCallInfo(thread, propDesc.GetSetter(), objHandle, JSTaggedValue::Undefined(), 1);
    info->SetCallArgs(valueHandle);
    JSTaggedValue res = JSFunction::Call(info.Get());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res;
}

JSTaggedValue SlowRuntimeStub::ClassPrivateFieldIn(JSThread *thread, JSTaggedValue ctor, JSTaggedValue obj,
                                                   JSTaggedValue propName)
{
    INTERPRETER_TRACE(thread, ClassPrivateFieldIn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    PropertyDescriptor propDesc(thread);
    [[maybe_unused]] PrivateFieldKind kind;

    if (!obj.IsECMAObject()) {
        return ThrowTypeError(thread, "Cannot use 'in' operator in Non-Object");
    }

    JSHandle<JSObject> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propNameHandle(thread, propName);

    JSHandle<JSTaggedValue> privateSymbol(
        thread, SlowRuntimeHelper::FindPrivateKey(thread, JSHandle<JSConstructorFunction>(thread, ctor), objHandle,
                                                  propNameHandle, propDesc, kind));

    return JSTaggedValue(!propDesc.GetValue()->IsHole());
}

JSTaggedValue SlowRuntimeStub::CreateRegExpWithLiteral(JSThread *thread, JSTaggedValue pattern, uint8_t flags)
{
    INTERPRETER_TRACE(thread, CreateRegExpWithLiteral);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> patternHandle(thread, pattern);
    JSHandle<JSTaggedValue> flagsHandle(thread, JSTaggedValue(flags));

    return builtins::reg_exp::RegExpCreate(thread, patternHandle, flagsHandle);
}

JSTaggedValue SlowRuntimeStub::CreateArrayWithBuffer(JSThread *thread, ObjectFactory *factory, JSArray *literal)
{
    INTERPRETER_TRACE(thread, CreateArrayWithBuffer);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSArray> array(thread, literal);
    JSHandle<JSArray> arrLiteral = factory->CloneArrayLiteral(array);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    return arrLiteral.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::GetMethod(JSThread *thread, JSTaggedValue object, JSTaggedValue propKey)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj(thread, object);
    JSHandle<JSTaggedValue> key(thread, propKey);

    JSHandle<JSTaggedValue> method = JSObject::GetMethod(thread, obj, key);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return method.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::GetTemplateObject(JSThread *thread, JSTaggedValue literal)
{
    INTERPRETER_TRACE(thread, GetTemplateObject);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> templateLiteral(thread, literal);
    JSHandle<JSTaggedValue> templateObj = TemplateString::GetTemplateObject(thread, templateLiteral);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return templateObj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::GetNextPropName(JSThread *thread, JSTaggedValue iter)
{
    INTERPRETER_TRACE(thread, GetNextPropName);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> iterator(thread, iter);
    ASSERT(iterator->IsForinIterator());
    std::pair<JSTaggedValue, bool> res =
        JSForInIterator::NextInternal(thread, JSHandle<JSForInIterator>::Cast(iterator));
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res.first;
}

JSTaggedValue SlowRuntimeStub::CopyDataProperties(JSThread *thread, JSTaggedValue dst, JSTaggedValue src)
{
    INTERPRETER_TRACE(thread, CopyDataProperties);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> dstHandle(thread, dst);
    JSHandle<JSTaggedValue> srcHandle(thread, src);
    if (!srcHandle->IsNull() && !srcHandle->IsUndefined()) {
        JSHandle<TaggedArray> keys = JSTaggedValue::GetOwnPropertyKeys(thread, srcHandle);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

        JSMutableHandle<JSTaggedValue> key(thread, JSTaggedValue::Undefined());
        uint32_t keysLen = keys->GetLength();
        for (uint32_t i = 0; i < keysLen; i++) {
            PropertyDescriptor desc(thread);
            key.Update(keys->Get(i));
            bool success = JSTaggedValue::GetOwnProperty(thread, srcHandle, key, desc);
            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

            if (success && desc.IsEnumerable()) {
                JSTaggedValue::DefineOwnProperty(thread, dstHandle, key, desc);
                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
            }
        }
    }
    return dstHandle.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::GetUnmappedArgs(JSThread *thread, uint32_t actualNumArgs, JSTaggedType *stkargs)
{
    INTERPRETER_TRACE(thread, GetUnmapedArgs);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<GlobalEnv> globalEnv = thread->GetEcmaVM()->GetGlobalEnv();
    JSHandle<TaggedArray> argumentsList = factory->NewTaggedArray(actualNumArgs);
    for (uint32_t i = 0; i < actualNumArgs; ++i) {
        argumentsList->Set(thread, i,
                           JSTaggedValue(stkargs[i]));  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    }
    // 1. Let len be the number of elements in argumentsList
    int32_t len = argumentsList->GetLength();
    // 2. Let obj be ObjectCreate(%ObjectPrototype%, «[[ParameterMap]]»).
    // 3. Set obj’s [[ParameterMap]] internal slot to undefined.
    JSHandle<JSArguments> obj = factory->NewJSArguments();
    // 4. Perform DefinePropertyOrThrow(obj, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: true,
    // [[Enumerable]]: false, [[Configurable]]: true}).
    obj->SetPropertyInlinedProps(thread, JSArguments::LENGTH_INLINE_PROPERTY_INDEX, JSTaggedValue(len));
    // 5. Let index be 0.
    // 6. Repeat while index < len,
    //    a. Let val be argumentsList[index].
    //    b. Perform CreateDataProperty(obj, ToString(index), val).
    //    c. Let index be index + 1
    obj->SetElements(thread, argumentsList.GetTaggedValue());
    // 7. Perform DefinePropertyOrThrow(obj, @@iterator, PropertyDescriptor
    // {[[Value]]:%ArrayProto_values%,
    // [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true}).
    obj->SetPropertyInlinedProps(thread, JSArguments::ITERATOR_INLINE_PROPERTY_INDEX,
                                 globalEnv->GetArrayProtoValuesFunction().GetTaggedValue());
    // 9. Perform DefinePropertyOrThrow(obj, "callee", PropertyDescriptor {[[Get]]: %ThrowTypeError%,
    // [[Set]]: %ThrowTypeError%, [[Enumerable]]: false, [[Configurable]]: false}).
    JSHandle<JSTaggedValue> throwFunction = globalEnv->GetThrowTypeError();
    JSHandle<AccessorData> accessor = factory->NewAccessorData();
    accessor->SetGetter(thread, throwFunction);
    accessor->SetSetter(thread, throwFunction);
    obj->SetPropertyInlinedProps(thread, JSArguments::CALLEE_INLINE_PROPERTY_INDEX, accessor.GetTaggedValue());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    // 11. Return obj
    return obj.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::CopyRestArgs(JSThread *thread, uint32_t restNumArgs, JSTaggedType *stkargs)
{
    INTERPRETER_TRACE(thread, Copyrestargs);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> restArray = JSArray::ArrayCreate(thread, JSTaggedNumber(restNumArgs));

    JSMutableHandle<JSTaggedValue> element(thread, JSTaggedValue::Undefined());
    for (uint32_t i = 0; i < restNumArgs; ++i) {
        // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
        element.Update(JSTaggedValue(stkargs[i]));
        JSObject::SetProperty(thread, restArray, i, element, true);
    }
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return restArray.GetTaggedValue();
}

// 7.4.1 GetIterator
JSTaggedValue SlowRuntimeStub::GetIterator(JSThread *thread, JSTaggedValue obj, bool async, JSTaggedValue method)
{
    INTERPRETER_TRACE(thread, GetIterator);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    EcmaVM *vm = thread->GetEcmaVM();
    auto globalConst = thread->GlobalConstants();
    JSHandle<GlobalEnv> env = vm->GetGlobalEnv();
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSMutableHandle<JSTaggedValue> methodHandle(thread, globalConst->GetHandledUndefined());

    // 3. If method is not present, then
    if (method.IsHole()) {
        // a. If hint is async, then
        if (async) {
            // i. Set method to ? GetMethod(obj, @@asyncIterator).
            methodHandle.Update(JSObject::GetMethod(thread, objHandle, env->GetAsyncIteratorSymbol()));
            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

            if (methodHandle->IsUndefined()) {
                // 1. Let syncMethod be ? GetMethod(obj, @@iterator).
                JSHandle<JSTaggedValue> syncMethod = JSObject::GetMethod(thread, objHandle, env->GetIteratorSymbol());
                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

                // 2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod).
                JSHandle<JSTaggedValue> syncIterator(thread,
                                                     SlowRuntimeStub::GetIterator(thread, objHandle.GetTaggedValue(),
                                                                                  false, syncMethod.GetTaggedValue()));
                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

                // 6. Let nextMethod be ? GetV(iterator, "next").
                JSHandle<JSTaggedValue> syncNextMethod =
                    JSTaggedValue::GetProperty(thread, syncIterator, globalConst->GetHandledNextString()).GetValue();
                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

                // 3. Return ! CreateAsyncFromSyncIterator(syncIteratorRecord).
                return JSAsyncFromSyncIteratorObject::CreateAsyncFromSyncIterator(thread, syncIterator, syncNextMethod);
            }
        } else {
            methodHandle.Update(JSTaggedValue::GetProperty(thread, objHandle, env->GetIteratorSymbol()).GetValue());
            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        }
    } else {
        methodHandle.Update(method);
    }

    // 4. Let iterator be ? Call(method, obj).
    auto info = NewRuntimeCallInfo(thread, methodHandle, objHandle, JSTaggedValue::Undefined(), 0);
    JSTaggedValue iterator = JSFunction::Call(info.Get());

    // 5. If Type(iterator) is not Object, throw a TypeError exception.
    if (!iterator.IsECMAObject()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "Iterator is not object", JSTaggedValue::Exception());
    }

    return iterator;
}

JSTaggedValue SlowRuntimeStub::DefineGetterSetterByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop,
                                                         JSTaggedValue getter, JSTaggedValue setter, bool flag)
{
    INTERPRETER_TRACE(thread, DefineGetterSetterByValue);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<JSObject> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, prop);

    JSHandle<JSTaggedValue> getterHandle(thread, getter);
    JSHandle<JSTaggedValue> setterHandle(thread, setter);
    JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, propHandle);

    auto globalConst = thread->GlobalConstants();
    if (objHandle.GetTaggedValue().IsClassConstructor() &&
        JSTaggedValue::SameValue(propKey, globalConst->GetHandledPrototypeString())) {
        return ThrowTypeError(
            thread,
            "In a class, computed property names for static getter that are named 'prototype' throw a TypeError");
    }

    if (flag) {
        if (!getterHandle->IsUndefined()) {
            if (propKey->IsNumber()) {
                propKey =
                    JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()));
            }
            JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(getterHandle), propKey,
                                            JSHandle<JSTaggedValue>(thread, globalConst->GetGetString()));
        }

        if (!setterHandle->IsUndefined()) {
            if (propKey->IsNumber()) {
                propKey =
                    JSHandle<JSTaggedValue>::Cast(base::NumberHelper::NumberToString(thread, propKey.GetTaggedValue()));
            }
            JSFunctionBase::SetFunctionName(thread, JSHandle<JSFunctionBase>::Cast(setterHandle), propKey,
                                            JSHandle<JSTaggedValue>(thread, globalConst->GetSetString()));
        }
    }

    // set accessor
    bool enumerable =
        !(objHandle.GetTaggedValue().IsClassPrototype() || objHandle.GetTaggedValue().IsClassConstructor());
    PropertyDescriptor desc(thread, true, enumerable, true);
    if (!getterHandle->IsUndefined()) {
        JSHandle<JSFunction>::Cast(getterHandle)->SetFunctionKind(thread, FunctionKind::GETTER_FUNCTION);
        desc.SetGetter(getterHandle);
    }
    if (!setterHandle->IsUndefined()) {
        JSHandle<JSFunction>::Cast(setterHandle)->SetFunctionKind(thread, FunctionKind::SETTER_FUNCTION);
        desc.SetSetter(setterHandle);
    }
    JSObject::DefineOwnProperty(thread, objHandle, propKey, desc);

    return objHandle.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::LdObjByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, bool callGetter,
                                            JSTaggedValue receiver)
{
    INTERPRETER_TRACE(thread, LdObjByIndexDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSTaggedValue res;
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    if (callGetter) {
        res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle);
    } else {
        res = JSTaggedValue::GetProperty(thread, objHandle, idx).GetValue().GetTaggedValue();
    }
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res;
}

JSTaggedValue SlowRuntimeStub::StObjByIndex(JSThread *thread, JSTaggedValue obj, uint32_t idx, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StObjByIndexDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSTaggedValue::SetProperty(thread, JSHandle<JSTaggedValue>(thread, obj), idx,
                               JSHandle<JSTaggedValue>(thread, value), true);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::LdObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter,
                                           JSTaggedValue receiver)
{
    INTERPRETER_TRACE(thread, LdObjByNameDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSTaggedValue res;
    if (callGetter) {
        res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle);
    } else {
        JSHandle<JSTaggedValue> propHandle(thread, prop);
        res = JSTaggedValue::GetProperty(thread, objHandle, propHandle).GetValue().GetTaggedValue();
    }
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res;
}

JSTaggedValue SlowRuntimeStub::StObjByName(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StObjByNameDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    JSTaggedValue::SetProperty(thread, objHandle, propHandle, valueHandle, true);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::LdObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop, bool callGetter,
                                            JSTaggedValue receiver)
{
    INTERPRETER_TRACE(thread, LdObjByValueDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSTaggedValue res;
    if (callGetter) {
        res = JSObject::CallGetter(thread, AccessorData::Cast(receiver.GetTaggedObject()), objHandle);
    } else {
        JSHandle<JSTaggedValue> propKey = JSTaggedValue::ToPropertyKey(thread, JSHandle<JSTaggedValue>(thread, prop));
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        res = JSTaggedValue::GetProperty(thread, objHandle, propKey).GetValue().GetTaggedValue();
    }
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res;
}

JSTaggedValue SlowRuntimeStub::StObjByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue prop,
                                            JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StObjByValueDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> valueHandle(thread, value);
    JSHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, propHandle));

    // strict mode is true
    JSTaggedValue::SetProperty(thread, objHandle, propKey, valueHandle, true);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::TryLdGlobalByName(JSThread *thread, JSTaggedValue global, JSTaggedValue prop)
{
    INTERPRETER_TRACE(thread, Trygetobjprop);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> obj(thread, global.GetTaggedObject()->GetClass()->GetPrototype());
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    OperationResult res = JSTaggedValue::GetProperty(thread, obj, propHandle);
    if (!res.GetPropertyMetaData().IsFound()) {
        return ThrowReferenceError(thread, propHandle.GetTaggedValue(), " is not defined");
    }
    return res.GetValue().GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::LdGlobalVar(JSThread *thread, JSTaggedValue global, JSTaggedValue prop)
{
    INTERPRETER_TRACE(thread, LdGlobalVar);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> objHandle(thread, global.GetTaggedObject()->GetClass()->GetPrototype());
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    OperationResult res = JSTaggedValue::GetProperty(thread, objHandle, propHandle);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res.GetValue().GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::StGlobalVar(JSThread *thread, JSTaggedValue prop, JSTaggedValue value)
{
    INTERPRETER_TRACE(thread, StGlobalVar);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> global(thread, thread->GetEcmaVM()->GetGlobalEnv()->GetGlobalObject());
    JSHandle<JSTaggedValue> propHandle(thread, prop);
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    JSObject::GlobalSetProperty(thread, propHandle, valueHandle, true);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::ThrowReferenceError(JSThread *thread, JSTaggedValue prop, const char *desc)
{
    INTERPRETER_TRACE(thread, ThrowReferenceError);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> propName = JSTaggedValue::ToString(thread, JSHandle<JSTaggedValue>(thread, prop));
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    JSHandle<EcmaString> info = factory->NewFromString(desc);
    JSHandle<EcmaString> msg = factory->ConcatFromString(propName, info);
    THROW_NEW_ERROR_AND_RETURN_VALUE(thread,
                                     factory->NewJSError(base::ErrorType::REFERENCE_ERROR, msg).GetTaggedValue(),
                                     JSTaggedValue::Exception());
}

JSTaggedValue SlowRuntimeStub::ThrowTypeError(JSThread *thread, const char *message)
{
    INTERPRETER_TRACE(thread, ThrowTypeError);
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    THROW_TYPE_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception());
}

JSTaggedValue SlowRuntimeStub::ThrowSyntaxError(JSThread *thread, const char *message)
{
    INTERPRETER_TRACE(thread, ThrowSyntaxError);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    THROW_SYNTAX_ERROR_AND_RETURN(thread, message, JSTaggedValue::Exception());
}

JSTaggedValue SlowRuntimeStub::StArraySpread(JSThread *thread, JSTaggedValue dst, JSTaggedValue index,
                                             JSTaggedValue src)
{
    INTERPRETER_TRACE(thread, StArraySpread);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> dstHandle(thread, dst);
    JSHandle<JSTaggedValue> srcHandle(thread, src);
    JSMutableHandle<JSTaggedValue> indexHandle(thread, index);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    if (srcHandle->IsUndefined()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "undefined is not iterable", JSTaggedValue::Exception());
    }
    if (srcHandle->IsNull()) {
        THROW_TYPE_ERROR_AND_RETURN(thread, "null is not iterable", JSTaggedValue::Exception());
    }
    ASSERT(dstHandle->IsJSArray());
    if (srcHandle->IsString()) {
        JSHandle<EcmaString> srcString = JSTaggedValue::ToString(thread, srcHandle);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        uint32_t dstLen = indexHandle.GetTaggedValue().GetInt();
        uint32_t strLen = srcString->GetLength();
        uint32_t propCounter = 0;
        for (uint32_t i = 0; i < strLen; i++, propCounter++) {
            uint16_t res = srcString->At<false>(i);
            if (UNLIKELY(utf::IsUTF16SurrogatePair(res))) {
                std::array<uint16_t, 2> resSurrogatePair {};
                resSurrogatePair[0] = srcString->At<false>(i);
                resSurrogatePair[1] = srcString->At<false>(i + 1);
                JSHandle<JSTaggedValue> strValueSurrogatePair(factory->NewFromUtf16Literal(resSurrogatePair.data(), 2));
                JSTaggedValue::SetProperty(thread, dstHandle, dstLen + propCounter, strValueSurrogatePair, true);
                i++;
            } else {
                JSHandle<JSTaggedValue> strValue(factory->NewFromUtf16Literal(&res, 1));
                JSTaggedValue::SetProperty(thread, dstHandle, dstLen + propCounter, strValue, true);
            }
            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        }
        return JSTaggedValue(dstLen + propCounter);
    }

    JSHandle<JSTaggedValue> iter;
    auto globalConst = thread->GlobalConstants();
    if (srcHandle->IsJSArrayIterator() || srcHandle->IsJSMapIterator() || srcHandle->IsJSSetIterator() ||
        srcHandle->IsIterator()) {
        iter = srcHandle;
    } else if (srcHandle->IsJSArray() || srcHandle->IsJSMap() || srcHandle->IsTypedArray() || srcHandle->IsJSSet()) {
        JSHandle<JSTaggedValue> valuesStr = globalConst->GetHandledValuesString();
        JSHandle<JSTaggedValue> valuesMethod = JSObject::GetMethod(thread, srcHandle, valuesStr);
        iter = JSIterator::GetIterator(thread, srcHandle, valuesMethod);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    } else {
        iter = JSIterator::GetIterator(thread, srcHandle);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    }

    JSHandle<JSTaggedValue> valueStr = globalConst->GetHandledValueString();
    PropertyDescriptor desc(thread);
    JSHandle<JSTaggedValue> iterResult;
    do {
        iterResult = JSIterator::IteratorStep(thread, iter);
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        if (iterResult->IsFalse()) {
            break;
        }
        bool success = JSTaggedValue::GetOwnProperty(thread, iterResult, valueStr, desc);
        if (success && desc.IsEnumerable()) {
            JSTaggedValue::DefineOwnProperty(thread, dstHandle, indexHandle, desc);
            int tmp = indexHandle->GetInt();
            indexHandle.Update(JSTaggedValue(tmp + 1));
        }
    } while (true);

    return indexHandle.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::DefineGeneratorFunc(JSThread *thread, JSMethod *method)
{
    INTERPRETER_TRACE(thread, DefineGeneratorFunc);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSFunction> jsFunc = factory->NewJSGeneratorFunction(method);
    ASSERT_NO_ABRUPT_COMPLETION(thread);

    // 26.3.4.3 prototype
    // Whenever a GeneratorFunction instance is created another ordinary object is also created and
    // is the initial value of the generator function's "prototype" property.
    JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
    JSHandle<JSObject> initialGeneratorFuncPrototype =
        factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
    JSObject::SetPrototype(thread, initialGeneratorFuncPrototype, env->GetGeneratorPrototype());
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    jsFunc->SetProtoOrDynClass(thread, initialGeneratorFuncPrototype);
    jsFunc->SetupFunctionLength(thread);

    return jsFunc.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::DefineAsyncGeneratorFunc(JSThread *thread, JSMethod *method)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSFunction> jsFunc = factory->NewJSAsyncGeneratorFunction(method);
    ASSERT_NO_ABRUPT_COMPLETION(thread);

    // 27.4.4.3 prototype
    // Whenever a AsyncGeneratorFunction instance is created another ordinary object is also created and
    // is the initial value of the generator function's "prototype" property.
    JSHandle<JSTaggedValue> objFun = env->GetObjectFunction();
    JSHandle<JSObject> initialAsyncGeneratorFuncPrototype =
        factory->NewJSObjectByConstructor(JSHandle<JSFunction>(objFun), objFun);
    JSObject::SetPrototype(thread, initialAsyncGeneratorFuncPrototype, env->GetAsyncGeneratorPrototype());
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    jsFunc->SetProtoOrDynClass(thread, initialAsyncGeneratorFuncPrototype);
    jsFunc->SetupFunctionLength(thread);

    return jsFunc.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::DefineAsyncFunc(JSThread *thread, JSMethod *method)
{
    INTERPRETER_TRACE(thread, DefineAsyncFunc);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSHClass> dynclass = JSHandle<JSHClass>::Cast(env->GetAsyncFunctionClass());
    JSHandle<JSFunction> jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::ASYNC_FUNCTION);
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    return jsFunc.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::DefineNCFuncDyn(JSThread *thread, JSMethod *method)
{
    INTERPRETER_TRACE(thread, DefineNCFuncDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSHClass> dynclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutProto());
    JSHandle<JSFunction> jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::ARROW_FUNCTION);
    jsFunc->SetupFunctionLength(thread);
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    return jsFunc.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::DefinefuncDyn(JSThread *thread, JSMethod *method)
{
    INTERPRETER_TRACE(thread, DefinefuncDyn);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSHClass> dynclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithProto());
    JSHandle<JSFunction> jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::BASE_CONSTRUCTOR);
    jsFunc->SetupFunctionLength(thread);
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    return jsFunc.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::SuperCall(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget,
                                         uint16_t argsCount, JSTaggedType *stkargs)
{
    INTERPRETER_TRACE(thread, SuperCall);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> funcHandle(thread, func);
    JSHandle<JSTaggedValue> newTargetHandle(thread, newTarget);

    JSHandle<JSTaggedValue> superFunc(thread, JSHandle<JSObject>::Cast(funcHandle)->GetPrototype(thread));
    ASSERT(superFunc->IsJSFunction());
    auto info = NewRuntimeCallInfo(thread, superFunc, JSTaggedValue::Undefined(), newTargetHandle, argsCount);
    info->SetCallArg(argsCount, stkargs);
    JSTaggedValue result = JSFunction::Construct(info.Get());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    return result;
}

JSTaggedValue SlowRuntimeStub::SuperCallSpread(JSThread *thread, JSTaggedValue func, JSTaggedValue newTarget,
                                               JSTaggedValue array)
{
    INTERPRETER_TRACE(thread, SuperCallSpread);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSTaggedValue> funcHandle(thread, func);
    JSHandle<JSTaggedValue> newTargetHandle(thread, newTarget);
    JSHandle<JSTaggedValue> jsArray(thread, array);

    JSHandle<JSTaggedValue> superFunc(thread, JSHandle<JSObject>::Cast(funcHandle)->GetPrototype(thread));
    ASSERT(superFunc->IsJSFunction());

    JSHandle<TaggedArray> argv(thread, GetCallSpreadArgs(thread, jsArray.GetTaggedValue()));

    auto info = NewRuntimeCallInfo(thread, superFunc, JSTaggedValue::Undefined(), newTargetHandle, argv->GetLength());
    info->SetCallArg(argv->GetLength(), argv->GetData());
    JSTaggedValue result = JSFunction::Construct(info.Get());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    return result;
}

JSTaggedValue SlowRuntimeStub::DefineMethod(JSThread *thread, JSMethod *method,
                                            const JSHandle<JSTaggedValue> &homeObject)
{
    INTERPRETER_TRACE(thread, DefineMethod);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ASSERT(homeObject->IsECMAObject());

    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<JSHClass> dynclass = JSHandle<JSHClass>::Cast(env->GetFunctionClassWithoutProto());
    JSHandle<JSFunction> jsFunc = factory->NewJSFunctionByDynClass(method, dynclass, FunctionKind::NORMAL_FUNCTION);
    jsFunc->SetHomeObject(thread, homeObject);
    jsFunc->SetupFunctionLength(thread);
    ASSERT_NO_ABRUPT_COMPLETION(thread);
    return jsFunc.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::LdSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key,
                                              JSTaggedValue thisFunc)
{
    INTERPRETER_TRACE(thread, LdSuperByValue);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ASSERT(thisFunc.IsJSFunction());
    // get Homeobject form function
    JSHandle<JSTaggedValue> homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject());

    if (obj.IsUndefined()) {
        return ThrowReferenceError(thread, obj, "this is uninitialized.");
    }
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, key);

    JSHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, propHandle));
    JSHandle<JSTaggedValue> superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject));
    JSTaggedValue::RequireObjectCoercible(thread, superBase);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    JSTaggedValue res = JSTaggedValue::GetProperty(thread, superBase, propKey, objHandle).GetValue().GetTaggedValue();
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return res;
}

JSTaggedValue SlowRuntimeStub::StSuperByValue(JSThread *thread, JSTaggedValue obj, JSTaggedValue key,
                                              JSTaggedValue value, JSTaggedValue thisFunc)
{
    INTERPRETER_TRACE(thread, StSuperByValue);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ASSERT(thisFunc.IsJSFunction());
    // get Homeobject form function
    JSHandle<JSTaggedValue> homeObject(thread, JSFunction::Cast(thisFunc.GetTaggedObject())->GetHomeObject());

    if (obj.IsUndefined()) {
        return ThrowReferenceError(thread, obj, "this is uninitialized.");
    }
    JSHandle<JSTaggedValue> objHandle(thread, obj);
    JSHandle<JSTaggedValue> propHandle(thread, key);
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    JSHandle<JSTaggedValue> propKey(JSTaggedValue::ToPropertyKey(thread, propHandle));
    JSHandle<JSTaggedValue> superBase(thread, JSTaggedValue::GetSuperBase(thread, homeObject));
    JSTaggedValue::RequireObjectCoercible(thread, superBase);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    // check may_throw is false?
    JSTaggedValue::SetProperty(thread, superBase, propKey, valueHandle, objHandle, true);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return JSTaggedValue::True();
}

JSTaggedValue SlowRuntimeStub::GetCallSpreadArgs(JSThread *thread, JSTaggedValue array)
{
    INTERPRETER_TRACE(thread, GetCallSpreadArgs);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();

    JSHandle<JSTaggedValue> jsArray(thread, array);
    uint32_t argvMayMaxLength = JSHandle<JSArray>::Cast(jsArray)->GetArrayLength();
    JSHandle<TaggedArray> argv = factory->NewTaggedArray(argvMayMaxLength);
    JSHandle<JSTaggedValue> itor = JSIterator::GetIterator(thread, jsArray);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSMutableHandle<JSTaggedValue> next(thread, JSTaggedValue::Undefined());
    JSMutableHandle<JSTaggedValue> nextArg(thread, JSTaggedValue::Undefined());
    size_t argvIndex = 0;
    while (true) {
        next.Update(JSIterator::IteratorStep(thread, itor).GetTaggedValue());
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        if (JSTaggedValue::SameValue(next.GetTaggedValue(), JSTaggedValue::False())) {
            break;
        }
        nextArg.Update(JSIterator::IteratorValue(thread, next).GetTaggedValue());
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
        argv->Set(thread, argvIndex++, nextArg);
    }

    argv = factory->CopyArray(argv, argvMayMaxLength, argvIndex);
    return argv.GetTaggedValue();
}

void SlowRuntimeStub::ThrowDeleteSuperProperty(JSThread *thread)
{
    INTERPRETER_TRACE(thread, ThrowDeleteSuperProperty);
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> info = factory->NewFromCanBeCompressString("Can not delete super property");
    JSHandle<JSObject> errorObj = factory->NewJSError(base::ErrorType::REFERENCE_ERROR, info);
    THROW_NEW_ERROR_AND_RETURN(thread, errorObj.GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::NotifyInlineCache(JSThread *thread, JSFunction *func, JSMethod *method)
{
    INTERPRETER_TRACE(thread, NotifyInlineCache);

    if (method->GetProfileSize() != 0) {
        thread->GetEcmaVM()->AddMethodToProfile(method);
    }

    if (method->GetICMapping() == nullptr) {
        return JSTaggedValue::Undefined();
    }
    uint32_t icSlotSize = method->GetSlotSize();
    static_assert(std::is_pointer<JSMethod::ICMappingType>::value);
    ASSERT(icSlotSize <= std::numeric_limits<std::remove_pointer<JSMethod::ICMappingType>::type>::max());
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    [[maybe_unused]] EcmaHandleScope handleScope(thread);

    JSHandle<JSFunction> funcHandle(thread, func);
    JSHandle<ProfileTypeInfo> profileTypeInfo = factory->NewProfileTypeInfo(icSlotSize);
    funcHandle->SetProfileTypeInfo(thread, profileTypeInfo.GetTaggedValue());

    return profileTypeInfo.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::ResolveClass(JSThread *thread, JSTaggedValue ctor, TaggedArray *literal,
                                            JSTaggedValue base, JSTaggedValue lexenv, ConstantPool *constpool)
{
    ASSERT(ctor.IsClassConstructor());
    JSHandle<JSFunction> cls(thread, ctor);
    JSHandle<TaggedArray> literalBuffer(thread, literal);
    JSHandle<JSTaggedValue> lexicalEnv(thread, lexenv);
    JSHandle<ConstantPool> constpoolHandle(thread, constpool);

    SetClassInheritanceRelationship(thread, ctor, base);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    uint32_t literalBufferLength = literalBuffer->GetLength();

    // only traverse the value of key-value pair
    for (uint32_t index = 1; index < literalBufferLength - 1; index += 2) {  // 2: key-value pair
        JSTaggedValue value = literalBuffer->Get(index);
        if (LIKELY(value.IsJSFunction())) {
            JSFunction::Cast(value.GetTaggedObject())->SetLexicalEnv(thread, lexicalEnv.GetTaggedValue());
            JSFunction::Cast(value.GetTaggedObject())->SetConstantPool(thread, constpoolHandle.GetTaggedValue());
        }
    }

    cls->SetResolved(thread);
    return cls.GetTaggedValue();
}

// clone class may need re-set inheritance relationship due to extends may be a variable.
JSTaggedValue SlowRuntimeStub::CloneClassFromTemplate(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base,
                                                      JSTaggedValue lexenv, ConstantPool *constpool)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();

    ASSERT(ctor.IsClassConstructor());
    JSHandle<JSTaggedValue> lexenvHandle(thread, lexenv);
    JSHandle<JSTaggedValue> constpoolHandle(thread, JSTaggedValue(constpool));
    JSHandle<JSTaggedValue> baseHandle(thread, base);

    JSHandle<JSFunction> cls(thread, ctor);

    JSHandle<JSObject> clsPrototype(thread, cls->GetFunctionPrototype());

    bool canShareHclass = false;
    if (cls->GetClass()->GetProto() == baseHandle.GetTaggedValue()) {
        canShareHclass = true;
    }

    JSHandle<JSFunction> cloneClass = factory->CloneClassCtor(cls, lexenvHandle, canShareHclass);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    JSHandle<JSObject> cloneClassPrototype =
        factory->CloneObjectLiteral(JSHandle<JSObject>(clsPrototype), lexenvHandle, constpoolHandle, canShareHclass);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    // After clone both, reset "constructor" and "prototype" properties.
    cloneClass->SetFunctionPrototype(thread, cloneClassPrototype.GetTaggedValue());
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    PropertyDescriptor ctorDesc(thread, JSHandle<JSTaggedValue>(cloneClass), true, false, true);
    JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(cloneClassPrototype),
                                         globalConst->GetHandledConstructorString(), ctorDesc);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);

    cloneClass->SetHomeObject(thread, cloneClassPrototype);

    if (!canShareHclass) {
        SetClassInheritanceRelationship(thread, cloneClass.GetTaggedValue(), baseHandle.GetTaggedValue());
        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    }

    return cloneClass.GetTaggedValue();
}

JSTaggedValue SlowRuntimeStub::SetClassInheritanceRelationship(JSThread *thread, JSTaggedValue ctor, JSTaggedValue base)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
    const GlobalEnvConstants *globalConst = thread->GlobalConstants();

    JSHandle<JSTaggedValue> cls(thread, ctor);
    ASSERT(cls->IsJSFunction());
    JSMutableHandle<JSTaggedValue> parent(thread, base);

    /*
     *         class A / class A extends null                             class A extends B
     *                                       a                                                 a
     *                                       |                                                 |
     *                                       |  __proto__                                      |  __proto__
     *                                       |                                                 |
     *       A            ---->         A.prototype                  A             ---->    A.prototype
     *       |                               |                       |                         |
     *       |  __proto__                    |  __proto__            |  __proto__              |  __proto__
     *       |                               |                       |                         |
     *   Function.prototype       Object.prototype / null            B             ---->    B.prototype
     */

    JSHandle<JSTaggedValue> parentPrototype;
    // hole means parent is not present
    if (parent->IsHole()) {
        JSHandle<JSFunction>::Cast(cls)->SetFunctionKind(thread, FunctionKind::CLASS_CONSTRUCTOR);
        parentPrototype = env->GetObjectFunctionPrototype();
        parent.Update(env->GetFunctionPrototype().GetTaggedValue());
    } else if (parent->IsNull()) {
        JSHandle<JSFunction>::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR);
        parentPrototype = JSHandle<JSTaggedValue>(thread, JSTaggedValue::Null());
        parent.Update(env->GetFunctionPrototype().GetTaggedValue());
    } else if (!parent->IsConstructor()) {
        return ThrowTypeError(thread, "parent class is not constructor");
    } else {
        JSHandle<JSFunction>::Cast(cls)->SetFunctionKind(thread, FunctionKind::DERIVED_CONSTRUCTOR);
        parentPrototype =
            JSTaggedValue::GetProperty(thread, parent, globalConst->GetHandledPrototypeString()).GetValue();
        if (!parentPrototype->IsECMAObject() && !parentPrototype->IsNull()) {
            return ThrowTypeError(thread, "parent class have no valid prototype");
        }
    }
    cls->GetTaggedObject()->GetClass()->SetPrototype(thread, parent);

    JSHandle<JSObject> clsPrototype(thread, JSHandle<JSFunction>(cls)->GetFunctionPrototype());
    clsPrototype->GetClass()->SetPrototype(thread, parentPrototype);

    return JSTaggedValue::Undefined();
}

JSTaggedValue SlowRuntimeStub::SetClassConstructorLength(JSThread *thread, JSTaggedValue ctor, JSTaggedValue length)
{
    ASSERT(ctor.IsClassConstructor());

    JSFunction *cls = JSFunction::Cast(ctor.GetTaggedObject());
    if (LIKELY(!cls->GetClass()->IsDictionaryMode())) {
        cls->SetPropertyInlinedProps(thread, JSFunction::LENGTH_INLINE_PROPERTY_INDEX, length);
    } else {
        const GlobalEnvConstants *globalConst = thread->GlobalConstants();
        cls->UpdatePropertyInDictionary(thread, globalConst->GetLengthString(), length);
    }
    return JSTaggedValue::Undefined();
}

JSTaggedValue SlowRuntimeStub::LdEvalVar(JSThread *thread, JSTaggedValue name, JSTaggedValue evalBindings)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<EcmaString> nameHandle(thread, name);
    JSHandle<JSArray> evalBindingsHandle(thread, evalBindings);

    uint32_t index = 0;
    JSMutableHandle<LexicalEnv> lexEnv(
        thread, JSArray::FastGetPropertyByValue(thread, JSHandle<JSTaggedValue>::Cast(evalBindingsHandle), index++));

    ArraySizeT size = evalBindingsHandle->GetArrayLength();

    while (index < size) {
        auto scope = JSHandle<TaggedArray>::Cast(
            JSArray::FastGetPropertyByValue(thread, JSHandle<JSTaggedValue>::Cast(evalBindingsHandle), index++));

        ArraySizeT slot = 0;
        for (ArraySizeT j = 0; j < scope->GetLength(); slot++) {
            JSTaggedValue binding = scope->Get(j++);

            if (binding.IsTrue()) {
                binding = scope->Get(j++);
            }

            if (EcmaString::StringsAreEqual(*nameHandle, static_cast<EcmaString *>(binding.GetHeapObject()))) {
                return lexEnv->GetProperties(slot);
            }
        }

        lexEnv.Update(lexEnv->GetParentEnv());
    }

    auto globalObj = thread->GetGlobalObject();

    bool found = false;
    JSTaggedValue result = FastRuntimeStub::GetGlobalOwnProperty(globalObj, nameHandle.GetTaggedValue(), &found);
    if (found) {
        return result;
    }
    return TryLdGlobalByName(thread, globalObj, nameHandle.GetTaggedValue());
}

JSTaggedValue SlowRuntimeStub::StEvalVar(JSThread *thread, JSTaggedValue name, JSTaggedValue evalBindings,
                                         JSTaggedValue value)
{
    [[maybe_unused]] EcmaHandleScope handleScope(thread);
    JSHandle<EcmaString> nameHandle(thread, name);
    JSHandle<JSArray> evalBindingsHandle(thread, evalBindings);
    JSHandle<JSTaggedValue> valueHandle(thread, value);

    uint32_t index = 0;
    JSMutableHandle<LexicalEnv> lexEnv(
        thread, JSArray::FastGetPropertyByValue(thread, JSHandle<JSTaggedValue>::Cast(evalBindingsHandle), index++));

    ArraySizeT size = evalBindingsHandle->GetArrayLength();

    while (index < size) {
        auto scope = JSHandle<TaggedArray>::Cast(
            JSArray::FastGetPropertyByValue(thread, JSHandle<JSTaggedValue>::Cast(evalBindingsHandle), index++));

        ArraySizeT slot = 0;
        for (ArraySizeT j = 0; j < scope->GetLength(); slot++) {
            JSTaggedValue binding = scope->Get(j++);
            bool isConst = false;

            if (binding.IsTrue()) {
                isConst = true;
                binding = scope->Get(j++);
            }

            if (EcmaString::StringsAreEqual(*nameHandle, static_cast<EcmaString *>(binding.GetHeapObject()))) {
                if (isConst) {
                    return ThrowTypeError(thread, "Assignment to constant variable");
                }
                lexEnv->SetProperties(thread, slot, valueHandle.GetTaggedValue());
                return valueHandle.GetTaggedValue();
            }
        }

        lexEnv.Update(lexEnv->GetParentEnv());
    }

    JSObject::GlobalSetProperty(thread, JSHandle<JSTaggedValue>::Cast(nameHandle), valueHandle, true);
    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
    return valueHandle.GetTaggedValue();
}

}  // namespace ark::ecmascript
